前幾天開始接觸 SQL,也從 users 和 notes 認識了 Table、Primary Key、Foreign Key 和 One-to-Many。到了這裡,已經知道資料怎麼查詢、怎麼新增,也開始知道資料表之間的關係該怎麼設計,但還有一個問題:這些資料到底要存在哪裡?
前面的 Note API 如果只是把資料放在 Node.js 的陣列裡,例如:
const notes = [];
程式執行期間確實可以新增、修改和刪除資料,但這些資料其實只存在目前這次程式執行的記憶體中。只要 Server 重開,原本的資料就會一起消失。
因此,實際的後端系統通常會把「處理資料」和「保存資料」分開。Node.js / Express 負責接收 Request、執行程式邏輯和回傳 Response,而 Database 則負責長期保存資料。
Client
↓
Node.js / Express
↓
Database
今天要做的事情,就是把前幾天學到的 SQL 和 Database Design,真正放進一個可以保存資料的 Database 裡。
Database 可以理解成專門用來儲存、管理和查詢資料的系統。
它和 Node.js 記憶體中的陣列最大的差別,在於資料不需要依賴目前這次程式執行。Node.js Server 關閉之後,只要 Database 本身還在,下一次啟動 Server 時仍然可以重新取得原本的資料。
以目前的 Note API 為例,原本可能是:
Client
↓
Node.js / Express
↓
記憶體中的 notes[]
使用 Database 之後會變成:
Client
↓
Node.js / Express
↓
Database
↓
users / notes
例如使用者新增一篇 Note,Express 收到 Request 後,會把資料寫入 Database;之後使用者再次查詢 Note,Express 再從 Database 把資料讀出來。
因此 Database 解決的不只是「資料放在哪裡」,更重要的是讓資料從程式本身獨立出來,成為一個可以持續保存、管理和查詢的資料來源。
開始接觸 Database 後,另一組很常出現的概念就是 SQL 和 NoSQL。
SQL Database 通常採用關聯式資料模型,資料會被放在 Table 中,不同 Table 之間可以透過 Primary Key 和 Foreign Key 建立關係。
前一天設計的 users 和 notes 就是一個典型例子:
users
id | name | email
notes
id | title | content | user_id
notes.user_id 可以對應到 users.id,因此一個 User 可以擁有多篇 Note。
NoSQL 則不是單一種類的 Database,而是一大類不同於傳統關聯式資料庫的資料庫模型,其中包含 Document、Key-Value、Wide-Column 和 Graph 等類型。
以常見的 Document Database 為例,資料可能會用接近 JSON 的形式儲存:
{
"id": 1,
"name": "Amy",
"email": "amy@example.com"
}
因此 SQL 和 NoSQL 的差異,不只是「有沒有使用 SQL」,更重要的是資料如何被組織,以及資料之間如何建立關係。
目前的 Note API 已經有明確的 User、Note,以及 User 和 Note 之間的關係,因此使用關聯式 Database 會很自然。
這時候又會出現另一個容易混淆的地方:如果前幾天學的是 SQL,為什麼現在又要學 PostgreSQL?
因為 SQL 是語言,而 PostgreSQL 是資料庫管理系統。
例如:
SELECT *
FROM notes;
這段是 SQL,描述的是「從 notes 查詢資料」。
真正負責儲存資料、執行 SQL、管理 Table,以及處理 Primary Key、Foreign Key 等 Constraint 的,則是 PostgreSQL。
可以簡單理解成:
SQL
→ 和 Database 溝通的語言
PostgreSQL
→ 實際儲存與管理資料的 Database 系統
所以前幾天寫的 SQL 並不是只能在 QueryPlane 這類 Playground 裡使用。SQL 最終是要交給某個 Database 系統執行,而今天我們選擇的就是 PostgreSQL。
目前這個學習專案選擇 PostgreSQL,首先是因為 Note API 本身就是很典型的關聯式資料結構。
我們已經有:
users
notes
而且兩者之間存在:
users.id
↑
│
notes.user_id
PostgreSQL 對 Primary Key、Foreign Key、UNIQUE、NOT NULL、Transaction 和 Index 等關聯式資料庫常用功能都有完整支援。
另一方面,PostgreSQL 也支援 JSON / JSONB 等資料型別,因此並不是只能處理非常固定的表格資料。
對目前這個學習專案來說,PostgreSQL 有一個很大的好處,就是可以把前幾天已經學過的 SQL 和 Database Design 直接延續下來,而不用重新學習另一套資料模型。
前一天使用 QueryPlane,是直接在瀏覽器裡建立資料表和測試 SQL。這種方式比較像 Playground,適合剛開始學習時快速測試。
現在則要把 PostgreSQL 真正安裝到自己的開發環境中,讓之後的 Node.js / Express 可以連接它。
這次以 macOS 為例,可以從 PostgreSQL 官方網站下載安裝程式。
進入 macOS 的下載頁後,可以選擇 PostgreSQL 的安裝程式。對第一次接觸 PostgreSQL 的人來說,直接使用圖形化安裝程式會比較容易。
安裝過程中會看到可以選擇的元件,通常維持預設即可。這些元件中,目前最重要的是:
PostgreSQL Server
→ 真正負責儲存與管理資料
pgAdmin 4
→ 用圖形介面管理 PostgreSQL
接著安裝程式會要求設定 PostgreSQL 的管理者帳號密碼。
預設的管理者帳號通常是:
postgres
例如:
Username: postgres
Password: ********
這個密碼需要記住,因為之後使用 pgAdmin 連接 PostgreSQL Server 時會用到。
安裝過程中也會看到 Port 設定:
5432
如果沒有特殊需求,可以直接使用預設的 Port。
因此 PostgreSQL 安裝完成後,可以先把目前的連線資訊理解成:
Host: localhost
Port: 5432
User: postgres
Password: 安裝時設定的密碼
這些資訊之後 Node.js 連接 PostgreSQL 時也會使用。
安裝完成後,可以在 macOS 的應用程式中找到 pgAdmin 4。
第一次開啟 pgAdmin 時,可能會要求設定一組 Master Password。這個密碼是 pgAdmin 自己使用的,和 PostgreSQL 的 postgres 密碼不同。
可以先把兩個密碼分開理解:
pgAdmin Master Password
→ pgAdmin 本身使用的密碼
postgres Password
→ PostgreSQL Server 的登入密碼
進入 pgAdmin 後,可以在左側看到:
Servers
展開之後,通常可以看到剛才安裝的 PostgreSQL Server。
Servers
└── PostgreSQL
第一次連線時,如果要求輸入密碼,就輸入安裝 PostgreSQL 時設定的 postgres 密碼。
這裡其實可以建立一個很重要的觀念:
PostgreSQL
→ 真正負責儲存資料
pgAdmin
→ 操作 PostgreSQL 的工具
所以資料不是「存進 pgAdmin」,而是存進 PostgreSQL。pgAdmin 只是提供一個比較容易操作 PostgreSQL 的介面。
notes_db連接 PostgreSQL 後,先建立一個給 Note API 使用的 Database。
在左側找到:
Servers
└── PostgreSQL
└── Databases
在 Databases 上按右鍵,選擇建立 Database。
將名稱設定為:
notes_db
建立完成後,就會看到:
Databases
└── notes_db
可以把 Database 想成一個存放相關資料的主要空間。
前一天設計的 users 和 notes,之後就會成為 notes_db 裡的 Table。
整體關係會變成:
PostgreSQL
└── notes_db
├── users
└── notes
建立好 notes_db 後,就可以在 pgAdmin 中開啟 Query Tool。
選擇 notes_db,開啟 Query Tool,就會看到 SQL 編輯器。
這裡和前幾天使用 QueryPlane 的方式很像,但現在 SQL 是直接交給自己電腦上的 PostgreSQL 執行。
把前一天設計好的 Schema 建立出來:
CREATE TABLE users (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE
);
CREATE TABLE notes (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
user_id BIGINT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
執行之後,PostgreSQL 就會真正建立兩張 Table。
回到左側展開:
notes_db
└── Schemas
└── public
└── Tables
就會看到:
users
notes
這時候可以再展開 users 和 notes,確認欄位是否建立成功。
users
├── id
├── name
└── email
notes
├── id
├── title
├── content
└── user_id
到了這裡,前一天的 Database Design 就真正變成 Database 裡的資料表了。
接下來可以先加入測試資料。
INSERT INTO users (name, email)
VALUES ('Amy', 'amy@example.com');
INSERT INTO notes (title, content, user_id)
VALUES ('第一篇筆記', '這是我的第一篇 Note', 1);
第一個 INSERT 會建立一個 User,第二個 INSERT 則建立一篇屬於這個 User 的 Note。
接著可以使用 SELECT 查詢:
SELECT *
FROM users;
以及:
SELECT *
FROM notes;
如果成功,就可以看到剛才新增的資料。
也可以直接透過 pgAdmin 查看 Table 中的資料。在 notes 上按右鍵,選擇:
View/Edit Data
→ All Rows
就可以用圖形介面查看目前的資料。
這也是 pgAdmin 很方便的地方:平常可以使用 SQL 操作 Database,需要查看資料時,也可以直接透過介面瀏覽。
現在可以重新回頭看最開始的問題。
如果資料是:
const notes = [];
那麼資料其實存在 Node.js 的記憶體裡:
Node.js
└── notes[]
Server 一旦停止,這些資料也就消失。
現在改成 PostgreSQL 後:
Node.js
↓
PostgreSQL
↓
notes
Node.js 只是負責處理 API,而 PostgreSQL 負責保存資料。
因此即使 Node.js Server 重開:
Node.js
↓
重新啟動
只要 PostgreSQL 裡的資料還存在:
PostgreSQL
└── notes
├── 第一篇筆記
└── ...
API 仍然可以重新把資料查詢出來。
這就是 Database 和單純使用程式記憶體保存資料最大的差別。
把這幾天的內容放在一起看,整個學習過程其實是一步一步往下建立的。
Day 13
SQL
↓
學會怎麼操作資料
Day 14
Database Design
↓
學會怎麼設計資料表與資料關係
Day 15
PostgreSQL
↓
讓資料真正被保存
QueryPlane 讓我們先快速測試 SQL 和資料表設計,而 PostgreSQL 則讓這些資料真正存在自己的開發環境中。
今天也第一次透過 pgAdmin 操作真正的 Database,從建立 Database、建立 Table,到新增和查詢資料,前幾天學到的 SQL 和 Database Design 開始連成一條完整的流程。
接下來,還需要把 Node.js / Express 和 PostgreSQL 接起來。
到時候整個架構會變成:
Client
↓
Node.js / Express
↓
PostgreSQL
↓
users / notes
這也代表 Note API 會從原本:
API
↓
const notes = []
逐漸變成:
API
↓
Database
↓
永久保存的資料
前幾天是在學資料怎麼操作,也開始理解資料表應該怎麼設計。今天則把這些概念真正放進 PostgreSQL,第一次建立自己的 Database、Table,並實際寫入和查詢資料。
SQL 是我們和 Database 溝通的語言,PostgreSQL 是真正負責儲存與管理資料的系統,而 pgAdmin 則提供了一個方便操作 PostgreSQL 的圖形介面。
從 QueryPlane 裡的練習,到自己的 PostgreSQL Database,前面的 SQL 和 Database Design 終於不只是練習,而開始成為一個真正可以被後端 API 使用的資料層。
前幾天是在學資料怎麼操作、怎麼設計,今天則是讓這些資料真正存在。